﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SofireXSqlEntityGenerator.Core;
using Sofire.Data;
using ICSharpCode.TextEditor.Document;
using System.Text.RegularExpressions;
using System.IO;

namespace SofireXSqlEntityGenerator.UI
{
    /*
     * 1、增加配置文件（实体名称后缀、是否自动识别表名和列表等配置信息）
     * 2、增加路径输出。
     * 3、考虑自动创建文件夹（VSE）
     */
    public partial class MainForm : Form
    {

        public readonly static Icon FormIcon = Icon.FromHandle(Properties.Resources.MainForm.GetHicon());
        private readonly ContextMenu TreeViewContextMenu;
        public MainForm()
        {
            InitializeComponent();
            GA.SetAppDirectory(Path.GetDirectoryName(Connect.AddInInstance.SatelliteDllPath));
            GlobalParameters.Init();
            this.StartPosition = FormStartPosition.CenterScreen;
            this.Icon = FormIcon;
            tview_Objects.ImageList = new ImageList();
            tview_Objects.ImageList.Images.Add(Properties.Resources.Table);
            tview_Objects.ImageList.Images.Add(Properties.Resources.View);
            tview_Objects.AfterCheck += new TreeViewEventHandler(tview_Objects_AfterCheck);
            tview_Objects.CheckBoxes = true;
            tview_Objects.MouseDown += new MouseEventHandler(tview_Objects_MouseDown);

            TreeViewContextMenu = new System.Windows.Forms.ContextMenu();
            TreeViewContextMenu.MenuItems.Add("实体生成器(&E)", Item_EntityGenerator).Shortcut = Shortcut.F1;
            TreeViewContextMenu.MenuItems.Add("SQL 生成器(&G)", Item_SqlGenerator).Shortcut = Shortcut.F3;
            //run.Visible = false;

            tview_Objects.ContextMenu = TreeViewContextMenu;

            textEditorControl1.TextEditorProperties.Font = new Font("YaHei Consolas Hybrid", 10F);
            textEditorControl1.Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy("TSQL");

            textEditorControl1.ActiveTextAreaControl.TextArea.KeyDown += new KeyEventHandler(TextArea_KeyDown);
            textEditorControl1.ActiveTextAreaControl.VScrollBar.ValueChanged += new EventHandler(VScrollBar_ValueChanged);
            textEditorControl1.ActiveTextAreaControl.HScrollBar.ValueChanged += new EventHandler(VScrollBar_ValueChanged);
        }

        void VScrollBar_ValueChanged(object sender, EventArgs e)
        {
            this.Text = (sender as ScrollBar).Value.ToString();
        }

        void TextArea_KeyDown(object sender, KeyEventArgs e)
        {
            if(e.KeyCode == Keys.F5)
            {
                tsb_Run_Click(tsb_Run, EventArgs.Empty);
                e.Handled = true;
                e.SuppressKeyPress = false;
            }
        }

        void tview_Objects_MouseDown(object sender, MouseEventArgs e)
        {
            if(e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                var node = tview_Objects.GetNodeAt(e.Location);
                tview_Objects.ContextMenu = null;
                if(node == null) return;
                if(node.Tag is OracleObjectType) tview_Objects.ContextMenu = TreeViewContextMenu;

                tview_Objects.SelectedNode = node;

                //   TreeViewContextMenu.Show(tview_Objects, e.Location);
            }
        }

        void Item_SqlGenerator(object sender, EventArgs e)
        {
            if(tview_Objects.SelectedNode.Tag is OracleObjectType)
            {
                SqlGeneratorForm form = new SqlGeneratorForm(this._Generator, tview_Objects.SelectedNode.Text);
                if(form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    WriteToEditor(form.ResultSql);
                }
            }
        }

        void Item_EntityGenerator(object sender, EventArgs e)
        {
            if(tview_Objects.SelectedNode.Tag is OracleObjectType)
            {
                EntityGeneratorForm form = new EntityGeneratorForm(this._Generator, tview_Objects.SelectedNode.Text);
                if(form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    //WriteToEditor(form.ResultSql);
                }
            }
        }
        void tview_Objects_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if(e.Node.Tag is OracleObjectType) return;
            this.CheckNodes(e.Node.Nodes, e.Node.Checked);
        }

        private void CheckNodes(TreeNodeCollection nodes, bool isChecked)
        {
            foreach(TreeNode node in nodes)
            {
                node.Checked = isChecked;
                if(node.Nodes.Count > 0) this.CheckNodes(node.Nodes, isChecked);
            }
        }

        QueryEngineBase _Engine;
        SqlGeneratorBase _Generator;
        private void EnabledControls(bool login)
        {
            for(int i = 1 ; i < toolStrip1.Items.Count ; i++)
            {
                toolStrip1.Items[i].Enabled = login;
            }
            textEditorControl1.Enabled = login;
            tview_Objects.Enabled = login;
            if(!login)
            {
                tview_Objects.Nodes.Clear();
            }
        }

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            if(GlobalParameters.SelectedProject == null)
            {
                this.ShowError("尚未选择一个项目，点击确定后关闭。");
                this.Close();
            }
            var connectinString = Properties.Settings.Default.ConnectionString;
            var provider = (QueryEngineProvider)Properties.Settings.Default.DatabaseProvider;
            if(!string.IsNullOrEmpty(connectinString))
            {
                this._Engine = VSEx.CreateEngine(provider, connectinString);
            }
            else
            {
                this.ShowLogin();
            }
            this.InitEngine();
        }

        private static readonly object DirectoryNodeTag = new object();

        private TreeNode FindDirectoryNode(TreeNodeCollection coll, string direName)
        {
            foreach(TreeNode node in coll)
            {
                if(node.Tag == DirectoryNodeTag && node.Text == direName) return node;
            }
            return null;
        }

        private TreeNode AppendNodeTo(TreeNode parent, string text, int imageIndex, object tag)
        {
            TreeNode node = tag == DirectoryNodeTag ? this.FindDirectoryNode(parent.Nodes, text) : null;
            if(node == null)
            {
                node = new TreeNode(text);
                parent.Nodes.Add(node);
                node.Tag = tag;
                node.ImageIndex = imageIndex;
                node.SelectedImageIndex = imageIndex;
            }
            return node;
        }

        private void InitObjects(DataTable table, OracleObjectType type)
        {
            tview_Objects.BeginUpdate();
            var parent = new TreeNode(type.ToString());
            tview_Objects.Nodes.Add(parent);
            var imageIndex = (int)type;
            parent.ImageIndex = imageIndex;
            parent.SelectedImageIndex = imageIndex;
            foreach(DataRow row in table.Rows)
            {
                var objectName = Convert.ToString(row[0]);
                var splicIndex = objectName.IndexOf('_');
                var childParent = parent;
                if(splicIndex > 0)
                {
                    var direName = objectName.Substring(0, splicIndex);
                    if(!(string.Equals(direName, "v", StringComparison.CurrentCultureIgnoreCase)
                        || string.Equals(direName, "t", StringComparison.CurrentCultureIgnoreCase)))
                        childParent = this.AppendNodeTo(childParent, direName, imageIndex, DirectoryNodeTag);
                }
                this.AppendNodeTo(childParent, objectName, imageIndex, type);

            }
            tview_Objects.EndUpdate();
        }

        private void InitEngine()
        {
            this.EnabledControls(false);
            VSEx.ColumnsCache.Clear();
            if(this._Engine != null && this.IsSuccess(this._Engine.TestConnection()))
            {
                switch(this._Engine.Provider)
                {
                    case QueryEngineProvider.SQLite:
                        this._Generator = new SQLiteGenerator(this._Engine);
                        break;
                    case QueryEngineProvider.Oracle:
                        this._Generator = new OracleGenerator(this._Engine);
                        break;
                    case QueryEngineProvider.Microsoft_SQL_Server:
                        this._Generator = new MsSqlGenerator(this._Engine);
                        break;
                }
                var r = this._Generator.GetUserTables();
                if(!this.IsSuccess(r)) return;
                this.InitObjects(r.Value, OracleObjectType.TABLE);

                r = this._Generator.GetUserViews();
                if(!this.IsSuccess(r)) return;
                this.InitObjects(r.Value, OracleObjectType.VIEW);

                this.EnabledControls(true);
            }
        }

        private void WriteToEditor(string context)
        {
            textEditorControl1.Text += "\r\n" + context;
            textEditorControl1.Refresh();
        }

        private void ShowLogin()
        {
            var loginForm = new LoginForm();
            if(loginForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this._Engine = loginForm.Engine;
            }
        }

        private void tsb_ConnectionManager_Click(object sender, EventArgs e)
        {
            this.ShowLogin();
            this.InitEngine();
        }

        private void tsb_Run_Click(object sender, EventArgs e)
        {
            var sqlCommandText = textEditorControl1.ActiveTextAreaControl.TextArea.SelectionManager.SelectedText;
            if(string.IsNullOrEmpty(sqlCommandText))
                sqlCommandText = textEditorControl1.Text;
            if(string.IsNullOrEmpty(sqlCommandText))
            {
                this.ShowError("请输入查询语句！");
                return;
            }
            var r = this._Engine.ExecuteTable(sqlCommandText);
            if(this.IsSuccess(r))
            {
                if(r.Value.Rows.Count == 1 && r.Value.Columns.Count == 1)
                {
                    tps_Tip.Text = Convert.ToString(r.Value.Rows[0][0]);
                    return;
                }
                new ResultDataForm(r).Show();
            }
        }

        public void GetSelectedObjects(List<string> tableNames, TreeNodeCollection nodes)
        {
            foreach(TreeNode node in nodes)
            {
                if(node.Checked && node.Tag is OracleObjectType)
                {
                    tableNames.Add(node.Text);
                    continue;
                }
                this.GetSelectedObjects(tableNames, node.Nodes);
            }
        }

        private void CopyTo(string from, string toDire, string toFile, string toExt)
        {
            int index = 0;
            var newPath = Path.Combine(toDire, toFile + toExt);
            while(File.Exists(newPath))
            {
                index++;
                newPath = Path.Combine(toDire, toFile + index + toExt);
            }
            File.Copy(from, newPath);
            var file = GlobalParameters.SelectedProject.ProjectItems.AddFromFile(newPath);
            file.Properties.Item("CopyToOutputDirectory").Value = (int)SofireXSqlEntityGenerator.GlobalActions.CopyToOutputDirectory.DoNotCopy;
            if(toExt == ".cs")
            {
                file.Properties.Item("ItemType").Value = SofireXSqlEntityGenerator.GlobalActions.BuildAction.None;
            }
        }
        private void tsb_Build_Click(object sender, EventArgs e)
        {
            bool buildXSql = GlobalSetting.EnabledXSql && this.ShowYesNo("是否输出 XSql 文件？");
            List<string> tableNames = new List<string>();
            try
            {
                GetSelectedObjects(tableNames, tview_Objects.Nodes);
                if(tableNames.Count == 0) return;
                var nowDire = Path.Combine(GlobalSetting.GetXSqlDirectory(), "_Old", DateTime.Now.ToString("yyyyMMdd HHmmssff"));
                if(!Directory.Exists(nowDire))
                {
                    Directory.CreateDirectory(nowDire);
                    //GlobalParameters.SelectedProject.ProjectItems.AddFromDirectory(nowDire);
                }
                foreach(var tableName in tableNames)
                {
                    var r = VSEx.RefreshColumns(this._Generator, tableName);
                    if(!this.IsSuccess(r))
                    {
                        this.ShowError(r);
                        return;
                    }

                    EntityGenerator gen = new EntityGenerator()
                    {
                        AllColumns = false,
                        AliasName = GlobalSetting.AutoAliasName ? VSEx.GetAliasName(tableName) : tableName,
                        ColumnsTable = r.Value,
                        Generator = this._Generator,
                        TableName = tableName
                    };

                    gen.ClassName = gen.AliasName + GlobalSetting.ModelSuffix;

                    var entityFilename = Path.Combine(GlobalParameters.OutputDirectory, gen.ClassName + ".cs");
                    if(File.Exists(entityFilename))
                    {
                        this.CopyTo(entityFilename, nowDire, gen.ClassName, ".cs");
                    }
                    string xsqlFilename = null;
                    if(buildXSql)
                    {
                        xsqlFilename = Path.Combine(GlobalSetting.GetXSqlDirectory(), gen.ClassName + ".xsql");
                        if(File.Exists(xsqlFilename))
                        {
                            this.CopyTo(xsqlFilename, nowDire, gen.ClassName, ".xsql");
                        }
                    }

                    gen.Write(entityFilename, xsqlFilename, false);

                }
            }
            catch(Exception ex)
            {
                File.AppendAllText(GA.AppDirectory + "\\error.txt", "\r\n" + ex.Message + "\r\n" + ex.StackTrace);
                this.ShowError(ex.Message);
            }
            if(VSEx.ShowYesNo("生成完毕！点击《是》关闭生成器。"))
            {
                this.Close();
            }
        }

        private void tsb_Setting_Click(object sender, EventArgs e)
        {
            new SettingForm().ShowDialog();
        }

        private void btn_Close_Click(object sender, EventArgs e)
        {
            this.Close();
        }

    }
}
